package org.budo.jdbc.dao.statement.named.parser;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.budo.jdbc.dao.statement.named.NamedParameterQuery;
import org.budo.support.lang.util.MapUtil;

/**
 * @author lmw
 */
public class DefaultNamedParameterQueryParser implements NamedParameterQueryParser {
    public NamedParameterQuery parseQuery(String sourceSql) {
        if (null == sourceSql) {
            return null;
        }

        List<String> parameters = new ArrayList<String>();
        int len = sourceSql.length();

        StringBuffer parsedSql = new StringBuffer(len);
        boolean inSingleQuote = false;
        boolean inDoubleQuote = false;
        boolean inSingleLineComment = false;
        boolean inMultiLineComment = false;
        boolean inDoubleСolon = false;

        for (int i = 0; i < len; i++) {
            char c = sourceSql.charAt(i);
            if (inSingleQuote) {
                if (c == '\'') {
                    inSingleQuote = false;
                }
            } else if (inDoubleQuote) {
                if (c == '"') {
                    inDoubleQuote = false;
                }
            } else if (inMultiLineComment) {
                if (c == '*' && sourceSql.charAt(i + 1) == '/') {
                    inMultiLineComment = false;
                }
            } else if (inDoubleСolon) {
                if (!Character.isJavaIdentifierPart(c)) {
                    inDoubleСolon = false;
                }
            } else if (inSingleLineComment) {
                if (c == '\n') {
                    inSingleLineComment = false;
                }
            } else {
                if (c == '\'') {
                    inSingleQuote = true;
                } else if (c == '"') {
                    inDoubleQuote = true;
                } else if (c == '/' && sourceSql.charAt(i + 1) == '*') {
                    inMultiLineComment = true;
                } else if (c == '-' && sourceSql.charAt(i + 1) == '-') {
                    inSingleLineComment = true;
                } else if (c == ':' && sourceSql.charAt(i + 1) == ':') {
                    inDoubleСolon = true;
                } else if (c == ':' && i + 1 < len && Character.isJavaIdentifierStart(sourceSql.charAt(i + 1))) {
                    int j = i + 2;
                    while (j < len && Character.isJavaIdentifierPart(sourceSql.charAt(j))) {
                        j++;
                    }

                    String name = sourceSql.substring(i + 1, j);
                    parameters.add(name);
                    c = '?'; // replace the parameter with a question mark
                    i += name.length(); // skip past the end if the parameter
                }
            }

            parsedSql.append(c);
        }

        Map<String, Integer> parameterIndexMap = MapUtil.valueToIndexMap(parameters);
        return new NamedParameterQuery(sourceSql, parsedSql.toString(), parameterIndexMap);
    }
}